/*
 * Copyright 2005, 2006 Alberto Jim??nez L??zaro
 *                      Pablo Galera Morcillo (umu-xacml-editor-admin@dif.um.es)
 *                      Dpto. de Ingenier??a de la Informaci??n y las Comunicaciones
 *                      (http://www.diic.um.es:8080/diic/index.jsp)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package org.umu.editorXacml3;

import org.xml.sax.SAXException;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;

/* ******************************************************************
 * Title: PrincipalPolitica
 *
 * Description: *//**
 * This is the main frame of the application.
 * 
 * @author Alberto Jimenez Lazaro y Pablo Galera Morcillo
 * @version 1.3
 *******************************************************************/
public class PrincipalPolicy extends JFrame {
	JPanel contentPane;
	JTree policyJTree;
	File inputFile;
	DefaultTreeModel miDTM = new DefaultTreeModel(new DefaultMutableTreeNode());
	JSplitPane jSplitPane1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
	JScrollPane jScrollPane1;
	BorderLayout borderLayout1 = new BorderLayout();

	JMenuBar menuBar = new JMenuBar();
	JMenu mnuFiles = new JMenu();
	JMenu mnuSchValidator = new JMenu();
	JMenu mnuAbout = new JMenu();

	JMenuItem mnuFiles_New = new JMenuItem();
	JMenuItem mnuFiles_Open = new JMenuItem();
	JMenuItem mnuFiles_Save = new JMenuItem();
	JMenuItem mnuFiles_SaveAs = new JMenuItem();
	JMenuItem mnuFiles_Exit = new JMenuItem();
	JMenuItem mnuItValidar = new JMenuItem();
	JMenuItem mnuItAbout = new JMenuItem();

	MiActionAdapter listener = new MiActionAdapter(this);
	VentanaMensajes vm = new VentanaMensajes();

	boolean muestraVersionWarning = true;

	public PrincipalPolicy(String f) throws SAXException, IOException {
		try {
			setDefaultCloseOperation(DISPOSE_ON_CLOSE);
			jbInit();
			AnalizadorSAX asax = new AnalizadorSAX();
			if (new File(f).exists()) {
				vm.getPrintStream().println("Analizing File: " + f);
				miDTM = (DefaultTreeModel) asax.analizeFile(f);
				policyJTree.setModel(miDTM);
			} else {
				actionPerformed(new ActionEvent(mnuFiles_New, 0, "mnuFiles_New"));
			}
			
			inputFile = new File(f);
			setTitle("UMU-XACML-Editor - " + inputFile.getName());
		} catch (SAXException exc) {
			// error message can be too long.
			(new JOptionPane()).showMessageDialog(this, exc.toString());
			throw exc;
		} catch (IOException exc) {
			(new JOptionPane()).showMessageDialog(this, exc.toString());
			throw exc;
		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}

	public PrincipalPolicy() {
		try {
			setDefaultCloseOperation(EXIT_ON_CLOSE);
			jbInit();
			actionPerformed(new ActionEvent(mnuFiles_New, 0, "mnuFiles_New"));
		} catch (Exception exception) {
			exception.printStackTrace();
		}
	}

	/**
	 * Component initialization.
	 * 
	 * @throws java.lang.Exception
	 */
	private void jbInit() throws Exception {
		this.addWindowListener(new MiWindowAdapter(this));
		policyJTree = new JTree(miDTM);
		policyJTree.setCellRenderer(new MiRenderer());
		contentPane = (JPanel) getContentPane();
		// JR: I know this was generated by JBuilder, but adding contentPane
		// allows
		// the JDK 1.4 targeted class files (generated by a JDK 1.5 compiler so
		// String.replace is usable) to run on JDK 1.4
		// this.setLayout(borderLayout1);
		contentPane.setLayout(borderLayout1);

		setSize(new Dimension(1024, 600));
		if (inputFile != null) {
			setTitle("UMU-XACML-Editor - " + inputFile.getName());
		} else {
			setTitle("UMU-XACML-Editor - No file is selected");
		}
		policyJTree.setToolTipText("");
		policyJTree.addMouseListener(new MiMouseAdapter(this));
		policyJTree
				.addTreeSelectionListener(new MiTreeSelectionAdapter(this));
		jScrollPane1 = new JScrollPane(policyJTree);
		jSplitPane1.setLeftComponent(jScrollPane1);
		jSplitPane1.setRightComponent(new JPanel());
		jSplitPane1.setResizeWeight(0.25);
		mnuFiles.setText("File");
		mnuSchValidator.setText("Schema Validator");
		mnuAbout.setText("About");
		mnuFiles_New.setText("New");
		mnuFiles_New.addActionListener(new MiActionAdapter(this));
		mnuFiles_Open.setText("Open...");
		mnuFiles_Open.addActionListener(new MiActionAdapter(this));
		mnuFiles_Save.setText("Save");
		mnuFiles_Save.addActionListener(new MiActionAdapter(this));
		mnuFiles_SaveAs.setText("Save As...");
		mnuFiles_SaveAs.addActionListener(new MiActionAdapter(this));
		mnuFiles_Exit.setText("Exit");
		mnuFiles_Exit.addActionListener(new MiActionAdapter(this));
		mnuItValidar.setText("Checking...");
		mnuItValidar.addActionListener(new MiActionAdapter(this));
		mnuItAbout.setText("Credits...");
		mnuItAbout.addActionListener(new MiActionAdapter(this));

		contentPane.add(jSplitPane1, borderLayout1.CENTER);
		JScrollPane jsp = new JScrollPane(vm);
		jsp.setPreferredSize(new Dimension(this.getWidth(), (int) (this
				.getHeight() * 0.25)));
		contentPane.add(jsp, borderLayout1.SOUTH);
		mnuFiles.add(mnuFiles_New);
		mnuFiles.add(mnuFiles_Open);
		mnuFiles.add(mnuFiles_Save);
		mnuFiles.add(mnuFiles_SaveAs);
		mnuFiles.addSeparator();
		mnuFiles.add(mnuFiles_Exit);
		mnuSchValidator.add(mnuItValidar);
		mnuAbout.add(mnuItAbout);
		menuBar.add(mnuFiles);
		menuBar.add(mnuSchValidator);
		menuBar.add(mnuAbout);
		this.setJMenuBar(menuBar);
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == mnuFiles_New) {
			saveChanged();
			DefaultMutableTreeNode polDoc = new DefaultMutableTreeNode(
					new String("Policy Document"));
			DefaultTreeModel auxdtm = new DefaultTreeModel(polDoc);
			miDTM = auxdtm;
			policyJTree.setModel(miDTM);
			inputFile = null;
			setTitle("UMU-XACML-Editor - No file is selected");

		} else if (e.getSource() == mnuFiles_Open) {
			saveChanged();
			JFileChooser jfChooser = new JFileChooser();
			jfChooser.setMultiSelectionEnabled(false);
			jfChooser.setCurrentDirectory(CurrentPath.getInstancia()
					.getCurrdir());
			jfChooser.setFileFilter(new XMLFileFilter("xml"));
			if (jfChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
				File temporal = jfChooser.getSelectedFile();
				AnalizadorSAX asax = new AnalizadorSAX();
				try {
					if (!temporal.toString().endsWith(".xml")) {
						JOptionPane.showMessageDialog(this,
								"The open File is not a Policy *.xml",
								"Error of Selection",
								JOptionPane.WARNING_MESSAGE);
					} else {

						CurrentPath.getInstancia().setCurrdir(
								temporal.getParentFile());
						vm.getPrintStream().println(
								"Analizing File: "
										+ temporal.getAbsolutePath());
						miDTM = (DefaultTreeModel) asax.analizeFile(temporal
								.getAbsolutePath());
						if (!asax.getErrorHandler().equalsIgnoreCase("")) {
							JOptionPane.showMessageDialog(this,
									asax.getErrorHandler(),
									"Errors produced in the parser",
									JOptionPane.WARNING_MESSAGE);
							vm.getPrintStream().println(asax.getErrorHandler());
						}
						inputFile = temporal;
						setTitle("UMU-XACML-Editor - "
								+ inputFile.getName());

						policyJTree.setModel(miDTM);
					}
				} catch (SAXException exc) {
					(new JOptionPane()).showMessageDialog(this, exc.toString());
					vm.getPrintStream().println(exc.toString());
				} catch (IOException exc) {
					(new JOptionPane()).showMessageDialog(this, exc.toString());
					vm.getPrintStream().println(exc.toString());
				}
			}
		} else if (e.getSource() == mnuFiles_Save) {
			AnalizadorSAX asax = new AnalizadorSAX();
			if (inputFile == null) {
				actionPerformed(new ActionEvent(mnuFiles_SaveAs, 0,
						"mnuFiles_SaveAs"));
			} else {
				vm.getPrintStream().println(
						"Process file:" + inputFile.getAbsolutePath());
				asax.saveToXacml((DefaultMutableTreeNode) miDTM.getRoot(),
						inputFile.getAbsolutePath());
			}
		} else if (e.getSource() == mnuFiles_SaveAs) {
			JFileChooser jfChooser = new JFileChooser();
			jfChooser.setCurrentDirectory(CurrentPath.getInstancia()
					.getCurrdir());
			jfChooser.setFileFilter(new XMLFileFilter("xml"));
			jfChooser.setMultiSelectionEnabled(false);

			if (jfChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
				File temporal = jfChooser.getSelectedFile();
				if (temporal.exists()) {
					if ((new XMLFileFilter("xml")).accept(temporal)) {
						int resp = JOptionPane
								.showConfirmDialog(
										this,
										"The File already exists. Do you wish to change (overwrite) it?",
										"Saving...", JOptionPane.YES_NO_OPTION,
										JOptionPane.QUESTION_MESSAGE);
						if (resp == JOptionPane.YES_OPTION) {
							AnalizadorSAX asax = new AnalizadorSAX();
							inputFile = temporal;
							vm.getPrintStream().println(
									"Process file:"
											+ inputFile.getAbsolutePath());
							asax.saveToXacml(
									(DefaultMutableTreeNode) miDTM.getRoot(),
									inputFile.getAbsolutePath());
							setTitle("UMU-XACML-Editor - "
									+ inputFile.getName());
						}
					}
				} else {
					if (temporal.getAbsolutePath().endsWith(".xml")) {
						inputFile = temporal;
					} else {
						inputFile = new File(temporal.getAbsolutePath()
								+ ".xml");
					}
					AnalizadorSAX asax = new AnalizadorSAX();
					vm.getPrintStream().println(
							"Process file:" + inputFile.getAbsolutePath());
					asax.saveToXacml(
							(DefaultMutableTreeNode) miDTM.getRoot(),
							inputFile.getAbsolutePath());
					setTitle("UMU-XACML-Editor - " + inputFile.getName());
				}

			}
		} else if (e.getSource() == mnuFiles_Exit) {
			saveChanged();
			this.dispose();
		} else if (e.getActionCommand().equalsIgnoreCase("copy")) {
			DefaultMutableTreeNode copia = (DefaultMutableTreeNode) policyJTree
					.getLastSelectedPathComponent();
			if (copia != null && !copia.isRoot()) {
				CurrentCopia.getInstancia().setCurrNode(copyNode(copia));
			}
		} else if (e.getActionCommand().equalsIgnoreCase("paste")) {
			DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) policyJTree
					.getLastSelectedPathComponent();
			if (selectedNode != null) {
				ElementoXACML newElem = (ElementoXACML) CurrentCopia
						.getInstancia().getCurrNode().getUserObject();
				DefaultMutableTreeNode newNode = copyNode(CurrentCopia
						.getInstancia().getCurrNode());

				Object nodeObj = selectedNode.getUserObject();
				if (nodeObj instanceof ElementoXACML) {
					ElementoXACML nodeElem = (ElementoXACML) nodeObj;
					if (nodeElem.getMaxNumChild(newElem) == 1) {
						if (overwriteOnly(newElem, selectedNode)) {
							ElementInsertOrder ioe = new ElementInsertOrder(
									selectedNode, newNode);
							ioe.ordenarInsercion();
							int pos = ioe.getPosicion();
							miDTM.insertNodeInto(newNode, selectedNode, pos);
							policyJTree.setModel(miDTM);
						}
					} else {
						ElementInsertOrder ioe = new ElementInsertOrder(
								selectedNode, newNode);
						ioe.ordenarInsercion();
						int pos = ioe.getPosicion();
						miDTM.insertNodeInto(newNode, selectedNode, pos);
						policyJTree.setModel(miDTM);

					}
				} else {
					if (overwriteOnly(newElem, selectedNode)) {
						miDTM.insertNodeInto(newNode, selectedNode,
								selectedNode.getChildCount());
						policyJTree.setModel(miDTM);
						miDTM.reload(selectedNode);
					}
				}
			}
		}

		else if (e.getSource() == mnuItValidar) {
			ValidatorDialog validador = new ValidatorDialog(vm.getPrintStream());
			// Center the window
			Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
			Dimension frameSize = validador.getSize();
			if (frameSize.height > screenSize.height) {
				frameSize.height = screenSize.height;
			}
			if (frameSize.width > screenSize.width) {
				frameSize.width = screenSize.width;
			}
			validador.setLocation((screenSize.width - frameSize.width) / 2,
					(screenSize.height - frameSize.height) / 2);

			validador.setModal(true);
			validador.setVisible(true);
		}

		else if (e.getSource() == mnuItAbout) {
			Creditos creditos = new Creditos();
			// Center the window
			Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
			Dimension frameSize = creditos.getSize();
			if (frameSize.height > screenSize.height) {
				frameSize.height = screenSize.height;
			}
			if (frameSize.width > screenSize.width) {
				frameSize.width = screenSize.width;
			}
			creditos.setLocation((screenSize.width - frameSize.width) / 2,
					(screenSize.height - frameSize.height) / 2);
			creditos.setModal(true);
			creditos.setVisible(true);
		}

		else if (e.getActionCommand().startsWith("add ")) {
			DefaultMutableTreeNode nodo = (DefaultMutableTreeNode) policyJTree
					.getLastSelectedPathComponent();
			if (nodo != null) {
				String nodoName = e.getActionCommand().replaceFirst("add ", "");
				/*if (nodoName.startsWith("Any") && muestraVersionWarning) {
					int resp = JOptionPane
							.showConfirmDialog(
									this,
									nodoName
											+ " is available in XACML 1.0 but not available in XACML 2.0, "
											+ "do you wish to add it?",
									"Warning", JOptionPane.YES_NO_OPTION,
									JOptionPane.QUESTION_MESSAGE);
					if (resp != JOptionPane.YES_OPTION) {
						return;
					}
					muestraVersionWarning = false;
				}*/ // code fragment disabled for XACML 3.0 modification
				createNodes(nodoName, nodo);
				if (!nodo.isRoot()) {
					((ElementoXACML) nodo.getUserObject()).setEmpty(false);
				}
			}
			// Para corregir una peque???a paradoja y evitar que salgan 2
			// Description
			this.valueChanged(new TreeSelectionEvent(this, null, null, null,
					null));
		}

		else if (e.getActionCommand().startsWith("remove")) {
			DefaultMutableTreeNode nodo = (DefaultMutableTreeNode) policyJTree
					.getLastSelectedPathComponent();
			if (nodo != null) {
				eliminarNodos(nodo);
			}
		}
	}

	private void saveChanged() {
		if (miDTM.getChildCount(miDTM.getRoot()) > 0) {
			int resp = JOptionPane.showConfirmDialog(this,
					"Not Save. Do you wish to save the changes?", "Saving...",
					JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
			if (resp == JOptionPane.YES_OPTION) {
				actionPerformed(new ActionEvent(mnuFiles_Save, 0, "mnuFiles_Save"));
			}
		}

	}

	private DefaultMutableTreeNode copyNode(DefaultMutableTreeNode node) {

		ElementoXACML aux = (ElementoXACML) ((ElementoXACML) node
				.getUserObject()).clone();
		DefaultMutableTreeNode nodoPadre = new DefaultMutableTreeNode(aux);

		Enumeration hijos = node.children();
		while (hijos.hasMoreElements()) {
			DefaultMutableTreeNode nodoHijo = copyNode((DefaultMutableTreeNode) hijos
					.nextElement());
			miDTM.insertNodeInto(nodoHijo, nodoPadre, nodoPadre.getChildCount());
		}
		return nodoPadre;
	}

	private void eliminarNodos(DefaultMutableTreeNode node) {
		DefaultMutableTreeNode padre = (DefaultMutableTreeNode) node
				.getParent();
		miDTM.removeNodeFromParent(node);
		if (padre.getChildCount() == 0) {
			if (padre.getUserObject() instanceof ElementoXACML) {
				((ElementoXACML) padre.getUserObject()).setEmpty(true);
			}
		}
		miDTM.reload(padre);
	}

	private boolean overwriteOnly(ElementoXACML elem,
			DefaultMutableTreeNode nodo) {
		Enumeration hijos = nodo.children();
		while (hijos.hasMoreElements()) {
			DefaultMutableTreeNode nodoHijo = (DefaultMutableTreeNode) hijos
					.nextElement();
			ElementoXACML elemHijo = (ElementoXACML) nodoHijo.getUserObject();
			if (elemHijo.getTipo() == elem.getTipo()) {
				int resp = JOptionPane
						.showConfirmDialog(
								this,
								elemHijo.getTipo()
										+ " is type unique. Do you wish to change (overwrite) it?",
								"overwriting...", JOptionPane.YES_NO_OPTION,
								JOptionPane.QUESTION_MESSAGE);
				if (resp == JOptionPane.YES_OPTION) {
					eliminarNodos(nodoHijo);
				} else {
					return false;
				}
			}
		}
		return true;
	}

	private void createNodes(String s, DefaultMutableTreeNode parentNode) {
		ElementoXACML newElem = ElementoXACMLFactoryImpl.getInstance()
				.obtenerElementoXACML(s, new Hashtable());
		if (newElem == null) {
			vm.getPrintStream().println("Element " + s + " not yet implement ");
			return;
		}
		newElem.setEmpty(true);
		DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(newElem);
		if (parentNode.getUserObject() instanceof ElementoXACML) {
			ElementInsertOrder ioe = new ElementInsertOrder(parentNode,
					newNode);
			ioe.ordenarInsercion();
			int pos = ioe.getPosicion();
			miDTM.insertNodeInto(newNode, parentNode, pos);
		} else {
			miDTM.insertNodeInto(newNode, parentNode, 0);
		}
		String requiredChildren[] = newElem.getAllObligatory();
		if (requiredChildren != null) {
			newElem.setEmpty(false);
			for (int i = 0; i < requiredChildren.length; i++) {
				createNodes(requiredChildren[i], newNode);
			}
		}
		if (parentNode.getUserObject() instanceof ElementoXACML) {
			miDTM.reload(parentNode);
		} else {
			miDTM.reload();
		}

	}

	public void valueChanged(TreeSelectionEvent e) {
		int original = jSplitPane1.getDividerLocation();
		DefaultMutableTreeNode selecto = (DefaultMutableTreeNode) policyJTree
				.getLastSelectedPathComponent();
		if (selecto != null) {
			ElementPanel aux = XACMLPanelFactoryImpl.getInstance()
					.obtenerPanel(selecto);

			if (aux != null) {
				jSplitPane1.setRightComponent(new JScrollPane(aux));
			}
			// La raiz PolicyDocument
			else if (aux == null && selecto.isRoot()) {
				jSplitPane1.setRightComponent(new JScrollPane(
						new PanelDocumento(inputFile, miDTM, vm
								.getPrintStream())));
			} else {
				jSplitPane1.setRightComponent(new JPanel());
			}

			if (aux instanceof ElementPanel) {
				aux.setTreeModel(this.miDTM);
			}
		} else {
			jSplitPane1.setRightComponent(new JPanel());
		}
		jSplitPane1.setDividerLocation(original);
	}

	public void mouseReleased(MouseEvent e) {
		DefaultMutableTreeNode nodo;
		if (SwingUtilities.isRightMouseButton(e)) {
			int xCoord = e.getX();
			int yCoord = e.getY();

			TreePath path = policyJTree.getPathForLocation(xCoord, yCoord);
			if (path != null) {
				policyJTree.setSelectionPath(path);
				nodo = (DefaultMutableTreeNode) path.getLastPathComponent();
				if (nodo != null) {
					JPopupMenu jppmMenuContext = MenuContextFactoryImpl
							.getInstance().obtenerMenuContext(nodo);
					if (jppmMenuContext != null) {
						MenuElement[] aux = jppmMenuContext.getSubElements();
						int i = 0;
						while (i < aux.length) {
							((JMenuItem) aux[i]).addActionListener(listener);
							i++;
						}
						jppmMenuContext
								.show(policyJTree, e.getX(), e.getY());
					}
				}
			}
		}
	}

	public void windowClosing(WindowEvent e) {
		if (miDTM.getChildCount(miDTM.getRoot()) > 0) {
			int resp = JOptionPane.showConfirmDialog(this,
					"Not Save. Do you wish to save the changes?", "Saving...",
					JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
			if (resp == JOptionPane.YES_OPTION) {
				actionPerformed(new ActionEvent(mnuFiles_Save, 0, "mnuFiles_Save"));
			}
		}
		this.dispose();
	}

}

class MiActionAdapter implements ActionListener {
	private PrincipalPolicy adaptee;

	MiActionAdapter(PrincipalPolicy adaptee) {
		this.adaptee = adaptee;
	}

	public void actionPerformed(ActionEvent e) {
		adaptee.actionPerformed(e);
	}
}

class MiTreeSelectionAdapter implements TreeSelectionListener {
	private PrincipalPolicy adaptee;

	MiTreeSelectionAdapter(PrincipalPolicy adaptee) {
		this.adaptee = adaptee;
	}

	public void valueChanged(TreeSelectionEvent e) {
		adaptee.valueChanged(e);
	}
}

class MiMouseAdapter extends MouseAdapter {
	private PrincipalPolicy adaptee;

	MiMouseAdapter(PrincipalPolicy adaptee) {
		this.adaptee = adaptee;
	}

	public void mouseReleased(MouseEvent e) {
		adaptee.mouseReleased(e);
	}
}

class MiWindowAdapter extends WindowAdapter {
	private PrincipalPolicy adaptee;

	MiWindowAdapter(PrincipalPolicy adaptee) {
		this.adaptee = adaptee;
	}

	public void windowClosing(WindowEvent e) {
		adaptee.windowClosing(e);
	}
}
